import React from 'react';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import Datum from './Datum';
import Checkbox from './Checkbox';

import './Tree.less';

class Tree extends React.Component {
    displayName = 'Tree';

    static childContextTypes = {
        onChangeChecked: PropTypes.func,
        onChangeOpened: PropTypes.func
    }

    getChildContext () {
        return {
            onChangeChecked: this.onChangeChecked,
            onChangeOpened: this.onChangeOpened
        };
    }

    static defaultProps = {
        level: 0
    }

    state = {
        selectedKey: ''
    }

    map = {};

    constructor (props) {
        super(props);

        const v = typeof props.value === 'string' ? props.value.split(',') : props.value || [];
        this.datum = new Datum({
            value: v,
            data: props.data,
            mode: props.mode || 0
        });
    }

    onSelect = (item) => {
        this.setState({
            selectedKey: item.id
        }, () => {
            if (this.props.onSelect) {
                this.props.onSelect(item);
            }
        });
    }

    onChangeChecked = (value, checkedId) => {
        if (this.props.onChange) {
            this.props.onChange(value, checkedId);
        }
    }

    onChangeOpened = (ids, id) => {
        if (this.props.onChangeOpened) {
            this.props.onChangeOpened(ids, id);
        }
    }

    componentDidUpdate (prevProps) {
        if (this.props.onChange) {
            this.datum.mode = this.props.mode || 0;
            if (prevProps.value !== this.props.value) {
                this.datum.setValue(this.props.value || []);
            }
            if (prevProps.data != this.props.data) {
                this.datum.setData(this.props.data);
            }
        }
        if (this.props.openedIds) {
            this.datum.setOpened(this.props.openedIds || '');
        }
        if (this.props.disabledIds !== undefined) {
            this.datum.setDisabled(this.props.disabledIds || '');
        }
    }

    render () {
        const {className, style, data, enableCheckbox} = this.props;
        const clazzName = classNames(className, 'cm-tree', {

        });
        return <div className={clazzName} style={style}>
            <SubNodes data={data} level={this.props.level} bindNode={this.bindNode} unbindNode={this.unbindNode}
                enableCheckbox={enableCheckbox} datum={this.datum}
                selectedKey={this.state.selectedKey} onSelect={this.onSelect}/>
        </div>;
    }
}

class Node extends React.PureComponent {
    displayName = 'Node';

    static contextTypes = {
        onChangeOpened: PropTypes.func
    }

    constructor (props) {
        super(props);
        this.state = {
            open: props.item.open,
            disabled: props.item.disabled
        };
        this.lastOpen = props.item.open;
        props.datum.bindNode(props.item.id, this.update.bind(this));
    }

    update (key, value) {
        if (this.state[key] !== value) { this.setState({ [key]: value }); }
    }

    componentWillUnmount () {
        this.props.datum.unbindNode(this.props.item.id);
    }

    onOpenClose = (item) => {
        item.open = !item.open;
        this.setState({
            open: item.open
        }, () => {
            const opened = this.props.datum.getOpened();
            this.context.onChangeOpened(opened, item.id);
        });
    }

    render () {
        const {item, selectedKey, style, enableCheckbox, datum, disabled} = this.props;
        
        const selected = `${item.id}` === selectedKey;
        const clazzName = classNames('cm-tree-item', {
            'cm-tree-item-open': this.state.open,
            'cm-tree-item-selected': selected
        });
        const hasChildren = item.children && item.children.length;
        let icon = hasChildren ? <span className='cm-tree-item-folder'></span>
            : <span className='cm-tree-item-file'></span>;
        if (item.icon) {
            icon = <span className='cm-tree-item-icon'>{item.icon}</span>;
        }
        return <li className={clazzName}>
            <div className='cm-tree-item-content' style={style}>
                <span className={`cm-tree-arrow ${hasChildren ? '' : 'hide'}`} onClick={this.onOpenClose.bind(this, item)}></span>
                {
                    enableCheckbox
                        ? <Checkbox datum={datum} key={item.id} id={item.id} disabled={disabled || this.state.disabled}/> : null
                }
                {icon}
                <span className='cm-tree-title'>
                    <span className='cm-tree-text' onClick={this.props.onSelect.bind(this, item)}>{item.text}</span>
                    {item.patch ? <span className='cm-tree-patch'>{item.patch}</span> : null}
                </span>
            </div>
            {item.children && item.children.length 
                ? <SubNodes selectedKey={this.props.selectedKey} onSelect={this.props.onSelect} 
                    enableCheckbox={enableCheckbox} datum={datum} disabled={disabled || this.state.disabled}
                    data={item.children} level={this.props.level + 1}/> : null}
        </li>;
    }
}

class SubNodes extends React.PureComponent {
    displayName = 'SubNodes';

    static defaultProps = {
        gutter: 24
    }

    renderNodes () {
        const style = {paddingLeft: this.props.level * this.props.gutter};
        return this.props.data.map(item => {
            return <Node {...this.props} key={item.id} item={item} style={style}/>;
        });
    }

    render () {
        const clazzName = classNames('cm-tree-nodes');
        return <ul className={clazzName} ref={f => this.box = f}>
            {this.renderNodes()}
        </ul>;
    }
}

export default Tree;
